package com.domino.client.mvc.impl;

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;

import com.domino.client.OthelloServiceAsync;
import com.domino.client.mvc.impl.ai.Bot;
import com.domino.client.mvc.impl.ai.BotFactory;
import com.domino.shared.ActionResponse;
import com.domino.shared.JoinRequest;
import com.domino.shared.JoinResponse;
import com.domino.shared.LeaveGameReason;
import com.domino.shared.Player;
import com.domino.shared.PlayerId;
import com.domino.shared.PutRequest;
import com.domino.shared.SessionStateRequest;
import com.domino.shared.SessionStateResponse;
import com.domino.shared.SessionStateResponseFinished;
import com.domino.shared.SessionStateResponseFirstRoundClientAction;
import com.domino.shared.SessionStateResponseFirstRoundWaitForServer;
import com.domino.shared.SessionStateResponseNewSession;
import com.domino.shared.SessionStateResponsePending;
import com.domino.shared.SessionStateResponsePlayerGaveUp;
import com.domino.shared.SessionStateResponseRoundClientAction;
import com.domino.shared.SessionStateResponseRoundWaitForServer;
import com.domino.shared.model.Board;
import com.domino.shared.model.Brick;
import com.domino.shared.model.Color;
import com.domino.shared.model.Point;
import com.domino.shared.model.SimpleBoard;
import com.domino.shared.session.SessionId;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class AutomaticOthelloServiceAsync implements OthelloServiceAsync {

	private boolean finished = false;
	private SimpleBoard board;
	private Color botColor;
	private Color humanColor;
	private Player opponent;
	private PlayerId humanPlayerId;
	private SessionStateResponse startState1;
	private SessionStateResponse startState2;
	private SessionId sessionId;
	private Bot bot;
	private OthelloServiceAsync remoteService;

	public AutomaticOthelloServiceAsync(SessionId oldId, int size,
			PlayerId humanPlayerId, Color clientColor,
			OthelloServiceAsync remoteService) {
		board = new SimpleBoard(size);
		this.humanColor = clientColor;
		this.botColor = Color.getOpposite(clientColor);
		bot = new BotFactory().createBot(botColor);
		opponent = bot.getPlayer();
		startState2 = init(clientColor, oldId);
		startState1 = new SessionStateResponseNewSession(
				startState2.getSessionId(), opponent, clientColor);
		this.remoteService = remoteService;
		this.humanPlayerId = humanPlayerId;
	}

	private SessionStateResponse init(Color clientColor, SessionId oldId) {
		sessionId = new SessionId(clientColor.name().substring(0, 1)
				+ oldId.getId());
		return Color.BLACK.equals(botColor) ? new SessionStateResponseFirstRoundWaitForServer(
				null, null) : new SessionStateResponseFirstRoundClientAction(
				null, null);
	}

	@Override
	public void putBrick(PutRequest request,
			AsyncCallback<ActionResponse> callback) {
		board.putBrick(request.getMove());

		ActionResponse result = new ActionResponse();
		callback.onSuccess(result);
	}

	@Override
	public void joinGame(JoinRequest request,
			AsyncCallback<JoinResponse> callback) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void pollSession(SessionStateRequest request,
			AsyncCallback<SessionStateResponse> callback) {
		if (finished == true) {
			callback.onSuccess(new SessionStateResponsePlayerGaveUp(
					this.humanColor));
			return;
		}
		if (startState1 != null) {
			callback.onSuccess(startState1);
			startState1 = null;
			return;
		}
		if (startState2 != null) {
			callback.onSuccess(startState2);
			startState2 = null;
			return;
		}
		boolean botTurn = isBotTurn();
		if (botTurn) {
			while (botTurn) {
				Brick move = getNextMove();
				List<Brick> moves = new LinkedList<Brick>();
				moves.add(move);
				board.putBrick(move);
				botTurn = isBotTurn();
				SessionStateResponse result = null;
				if (botTurn == true) {
					result = new SessionStateResponseRoundWaitForServer(moves);
				} else {
					result = canHumanMove() ? new SessionStateResponseRoundClientAction(
							moves) : finishGame(moves);
				}
				callback.onSuccess(result);
			}
		} else if (canHumanMove() == false) {
			callback.onSuccess(finishGame(null));
			return;
		} else {
			callback.onSuccess(new SessionStateResponseRoundClientAction(null));
		}
	}

	private SessionStateResponseFinished finishGame(List<Brick> moves) {
		if (canHumanMove() || canBotMove()) {
			throw new IllegalStateException("Spelet ska ju vara slut!!!");
		}
		if (remoteService != null) {
			try {
				this.remoteService.botGameFinished(sessionId,
						this.humanPlayerId, this.board.encodeBricks(), new AsyncCallback<Void>() {
							@Override
							public void onSuccess(Void result) {
								// do nothing
							}

							@Override
							public void onFailure(Throwable caught) {
								// do nothing
							}
						});
			} catch (Exception e) {
				// do nothing
			}
			this.remoteService = null;
		}
		return new SessionStateResponseFinished(moves);
	}

	private boolean canHumanMove() {
		return board.getAvailblePoints(this.humanColor).size() > 0;
	}

	private boolean canBotMove() {
		return board.getAvailblePoints(this.botColor).size() > 0;
	}
	
	private Brick getNextMove() {
		return bot.getNexMove(board);
	}

	private boolean isBotTurn() {
		if (board.getBricks().size() == 0) {
			return botColor.equals(Color.BLACK);
		}
		Color lastMoveColor = board.getBricks()
				.get(board.getBricks().size() - 1).getColor();
		if (lastMoveColor.equals(botColor)
				&& board.getAvailblePoints(humanColor).size() > 0) {
			return false;
		}
		return board.getAvailblePoints(this.botColor).size() > 0;
	}

	@Override
	public void getBricks(SessionId id, AsyncCallback<List<Brick>> callback) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void dummy(SessionStateResponseRoundWaitForServer a1,
			SessionStateResponseRoundClientAction a2,
			SessionStateResponsePending a3, SessionStateResponseNewSession a4,
			SessionStateResponseFirstRoundWaitForServer a5,
			SessionStateResponseFirstRoundClientAction a6,
			SessionStateResponseFinished a7,
			SessionStateResponsePlayerGaveUp a8, PlayerId id, SimpleBoard sb,
			LinkedList<Point> list, LinkedHashSet<Point> set,
			LinkedHashSet<Point> list2, LinkedList<Point> list3,
			AsyncCallback<Void> callback) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void leaveGame(SessionId id, PlayerId playerId,
			LeaveGameReason reason, AsyncCallback<Void> callback) {
		if (reason.equals(LeaveGameReason.PlayWithBot)) {
			throw new IllegalStateException("Is already playing with a bot");
		}
		finished = true;
		callback.onSuccess(null);
	}

	@Override
	public void botGameFinished(SessionId id, PlayerId playerId,String bricks,
			AsyncCallback<Void> callback) {
		throw new UnsupportedOperationException(
				"Supported by remote service only");
	}

}
